package com.security.it.authentication.filter;

import com.google.common.collect.Maps;
import com.security.it.authentication.common.ValidateCodeProcessorHolder;
import com.security.it.authentication.exception.ValidateCodeException;
import com.security.it.core.constants.SecurityConstants;
import com.security.it.core.enums.ValidateCodeType;
import com.security.it.core.properties.SecurityProperties;
import org.apache.commons.lang.StringUtils;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.security.web.authentication.AuthenticationFailureHandler;
import org.springframework.social.connect.web.HttpSessionSessionStrategy;
import org.springframework.social.connect.web.SessionStrategy;
import org.springframework.util.AntPathMatcher;
import org.springframework.web.context.request.ServletWebRequest;
import org.springframework.web.filter.OncePerRequestFilter;

import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.Arrays;
import java.util.Map;
import java.util.Set;

public class ValidateCodeFilter extends OncePerRequestFilter implements InitializingBean {

    private AuthenticationFailureHandler authenticationFailureHandler;

    private SessionStrategy sessionStrategy = new HttpSessionSessionStrategy();

    private SecurityProperties securityProperties;
    /**
     * 路径匹配规则
     */
    private AntPathMatcher pathMatcher = new AntPathMatcher();

    /**
     * 存放需要验证码的url
     */
    private Map<String, ValidateCodeType> urlMap = Maps.newHashMap();

    private ValidateCodeProcessorHolder validateCodeProcessorHolder;

    @Override
    public void afterPropertiesSet() throws ServletException {
        super.afterPropertiesSet();
        urlMap.put(SecurityConstants.DEFAULT_LOGIN_PROCESSING_URL, ValidateCodeType.IMAGE);
        addUrlToMap(securityProperties.getCode().getImage().getUrl(), ValidateCodeType.IMAGE);

        urlMap.put(SecurityConstants.DEFAULT_LOGIN_PROCESSING_URL_MOBILE, ValidateCodeType.SMS);
        addUrlToMap(securityProperties.getCode().getSms().getUrl(), ValidateCodeType.SMS);
    }

    private void addUrlToMap(String url, ValidateCodeType type) {
        if (StringUtils.isBlank(url)) {
            return;
        }
        String[] urls = StringUtils.splitByWholeSeparatorPreserveAllTokens(url, ",");
        Arrays.stream(urls).forEach(item -> urlMap.put(item, type));
    }

    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws IOException, ServletException {
        ValidateCodeType type = getValidateCodeType(request);

        if (null != type) {
            try {
                validateCodeProcessorHolder.findValidateCodeProcessor(type)
                        .validate(new ServletWebRequest(request, response));
            } catch (ValidateCodeException exception) {
                authenticationFailureHandler.onAuthenticationFailure(request, response, exception);
                return;
            } catch (Exception e) {
                e.printStackTrace();
                return;
            }
        }
        filterChain.doFilter(request, response);
    }

    /**
     * 获取url对应验证码类型
     */
    private ValidateCodeType getValidateCodeType(HttpServletRequest request) {
        String url = request.getRequestURI();
        Set<String> keys = urlMap.keySet();
        for (String key : keys) {
            if (pathMatcher.match(key, url)) {
                return urlMap.get(key);
            };
        }
        return null;
    }

    public void setAuthenticationFailureHandler(AuthenticationFailureHandler authenticationFailureHandler) {
        this.authenticationFailureHandler = authenticationFailureHandler;
    }

    public void setSecurityProperties(SecurityProperties securityProperties) {
        this.securityProperties = securityProperties;
    }

    public void setValidateCodeProcessorHolder(ValidateCodeProcessorHolder validateCodeProcessorHolder) {
        this.validateCodeProcessorHolder = validateCodeProcessorHolder;
    }
}
